package pl.poznan.put.cs.idss.viamdp.ui;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.peer.KeyboardFocusManagerPeer;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

import pl.poznan.put.cs.idss.viamdp.core.algorithm.IAlgorithm;
import pl.poznan.put.cs.idss.viamdp.core.structures.AvailableField;
import pl.poznan.put.cs.idss.viamdp.core.structures.ForbiddenField;
import pl.poznan.put.cs.idss.viamdp.core.structures.Map;

public class MainWindow extends JFrame implements ImageObserver, MouseListener,
		ComponentListener, KeyListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5855163647745371731L;

	private IAlgorithm algorithm;
	private Map worldMap;
	
	private boolean ifContinue = true;

	private JPanel panel = new JPanel();
	private Canvas canvas = new Canvas();
	Image disabledFieldImg;
	Image terminalFieldImg;
	Image holeFieldImg;
	Image topFieldImg;
	Image downFieldImg;
	Image leftFieldImg;
	Image rightFieldImg;

	int width = 580;
	int height = 620;

	public MainWindow(IAlgorithm algorithm, Map worldMap) {

		readImages();

		this.worldMap = worldMap;
		this.algorithm = algorithm;

		addMouseListener(this);
		addComponentListener(this);
		addKeyListener(this);

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setTitle("Value Iteration Algorithm - kliknij w oknie aby wykonać krok");
		setResizable(true);
		setSize(width, height);
		setVisible(true);
		canvas.setBounds(0, 0, width, height);
		canvas.setBackground(Color.WHITE);
		canvas.addMouseListener(this);
		panel.add(canvas);
		add(panel);

		Timer t = new Timer(20, new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				drawMap();
			}
		});
		
//		while(ifContinue) {
//			algorithm.doStep();
//		}

		t.start();

	}

	/**
	 * Wczytuje obrazy reprezentujące akcje w bieżącej polityce.
	 */
	private void readImages() {

		disabledFieldImg = Toolkit.getDefaultToolkit().getImage(
				(MainWindow.class.getResource("/disabled.png")));

		terminalFieldImg = Toolkit.getDefaultToolkit().getImage(
				(MainWindow.class.getResource("/terminal.png")));

		holeFieldImg = Toolkit.getDefaultToolkit().getImage(
				(MainWindow.class.getResource("/hole.png")));

		topFieldImg = Toolkit.getDefaultToolkit().getImage(
				(MainWindow.class.getResource("/top.png")));

		downFieldImg = Toolkit.getDefaultToolkit().getImage(
				(MainWindow.class.getResource("/down.png")));

		leftFieldImg = Toolkit.getDefaultToolkit().getImage(
				(MainWindow.class.getResource("/left.png")));

		rightFieldImg = Toolkit.getDefaultToolkit().getImage(
				(MainWindow.class.getResource("/right.png")));

	}

	private void drawMap() {

		canvas.setBounds(0, 0, width, height - 40);

		Image buffer = new BufferedImage(width, height - 40,
				BufferedImage.TYPE_INT_ARGB);

		Graphics gDest = canvas.getGraphics();
		Graphics g = buffer.getGraphics();

		int sizeX = worldMap.getSizeX();
		int sizeY = worldMap.getSizeY();

		int fieldSizeX = (height - 40) / sizeX;
		int fieldSizeY = width / sizeY;

		for (int i = 0; i < sizeX; i++) {
			for (int j = 0; j < sizeY; j++) {

				int posX = fieldSizeY * j;
				int posY = fieldSizeX * i;

				double value = worldMap.getFieldsArray()[i][j].getLastReward();

				g.setColor(getColorForValue(value));
				g.fillRect(posX, posY, fieldSizeY - 1, fieldSizeX - 1);

				String val = (Double.toString(value));
				if (value > 0) {
					val = "+" + val;
				}
				if (val.length() > 5) {
					val = val.substring(0, 6);
				} else {
					while (val.length() <= 5) {
						val += "0";
					}
				}

				g.setColor(Color.WHITE);
				g.drawString(val, posX + fieldSizeY - 50, posY + fieldSizeX - 8);
				g.drawRect(posX, posY, fieldSizeY - 1, fieldSizeX - 1);

				drawImages(g, fieldSizeX, fieldSizeY, i, j, posX, posY);

			}
		}

		gDest.drawImage(buffer, 0, 0, width, height - 40, this);

	}

	/**
	 * Rysuje obrazy zależnie od tego, jakiego typu jest pole.
	 * 
	 * @param g
	 * @param fieldSizeX
	 * @param fieldSizeY
	 * @param i
	 * @param j
	 * @param posX
	 * @param posY
	 */
	private void drawImages(Graphics g, int fieldSizeX, int fieldSizeY, int i,
			int j, int posX, int posY) {
		if (worldMap.getFieldsArray()[i][j] instanceof ForbiddenField) {
			g.setColor(new Color(150, 150, 150));
			g.fillRect(posX + 1, posY + 1, fieldSizeY - 2, fieldSizeX - 2);
		} else {
			if (worldMap.getFieldsArray()[i][j] instanceof AvailableField) {
				showPolicy(g, fieldSizeY, fieldSizeX, i, j, posX, posY);
			}
		}
	}

	/**
	 * Rysuje obrazy ukazujące bieżące polityki.
	 * 
	 * @param g
	 * @param fieldSizeY
	 * @param i
	 * @param j
	 * @param posX
	 * @param posY
	 */
	private void showPolicy(Graphics g, int fieldSizeY, int fieldSizeX, int i,
			int j, int posX, int posY) {
		double top = worldMap.getFieldsArray()[i][j]
				.getTopNeighbourReward(worldMap.getIteration());
		double bottom = worldMap.getFieldsArray()[i][j]
				.getBottomNeighbourReward(worldMap.getIteration());
		double left = worldMap.getFieldsArray()[i][j]
				.getLeftNeighbourReward(worldMap.getIteration());
		double right = worldMap.getFieldsArray()[i][j]
				.getRightNeighbourReward(worldMap.getIteration());
		if (top == ForbiddenField.FORBIDDEN_VALUE) {
			top = worldMap.getFieldsArray()[i][j].getLastReward();
		}
		if (bottom == ForbiddenField.FORBIDDEN_VALUE) {
			bottom = worldMap.getFieldsArray()[i][j].getLastReward();
		}
		if (left == ForbiddenField.FORBIDDEN_VALUE) {
			left = worldMap.getFieldsArray()[i][j].getLastReward();
		}
		if (right == ForbiddenField.FORBIDDEN_VALUE) {
			right = worldMap.getFieldsArray()[i][j].getLastReward();
		}
		double max = Math.max(Math.max(top, bottom), Math.max(left, right));
		int imgSize = (int) (Math.min(fieldSizeY, fieldSizeX) * 0.6);
		int halfImageSize = imgSize / 2;
		if (max == top) {
			g.drawImage(topFieldImg, posX + fieldSizeY / 2 - halfImageSize,
					posY + fieldSizeX / 2 - halfImageSize, imgSize, imgSize,
					this);
		} else if (max == bottom) {
			g.drawImage(downFieldImg, posX + fieldSizeY / 2 - halfImageSize,
					posY + fieldSizeX / 2 - halfImageSize, imgSize, imgSize,
					this);
		} else if (max == left) {
			g.drawImage(leftFieldImg, posX + fieldSizeY / 2 - halfImageSize,
					posY + fieldSizeX / 2 - halfImageSize, imgSize, imgSize,
					this);
		} else if (max == right) {
			g.drawImage(rightFieldImg, posX + fieldSizeY / 2 - halfImageSize,
					posY + fieldSizeX / 2 - halfImageSize, imgSize, imgSize,
					this);
		} else {

		}
	}

	private Color getColorForValue(double value) {
		double max = Math.abs(worldMap.getTerminalFieldValue()
				- worldMap.getHoleFieldValue());
		if (max == 0) {
			max = 0.1;
		}
		double val = value - worldMap.getHoleFieldValue();
		if (val < 0) {
			val = 0;
		}
		int to255 = (int) (255 * (val / max));
		if (to255 > 255) {
			to255 = 255;
		}
		if (to255 < 0) {
			to255 = 0;
		}
		return new Color(255 - to255, to255, 0);
	}

	public boolean imageUpdate(Image img, int infoflags, int x, int y,
			int width, int height) {
		return true;
	}

	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseReleased(MouseEvent e) {
		nextStepAction();
	}

	private void nextStepAction() {
		this.ifContinue = false;
		double error = algorithm.getMaxError();
		if (!algorithm.checkIfEnd()) {
			algorithm.doStep();
			this.setTitle("Iteracja: " + (worldMap.getIteration() - 1)
					+ " - kliknij, aby wykonać krok" + "; popełniany błąd: "
					+ error + " / dopuszczalny błąd: "
					+ worldMap.getIndifferenceThreshold());
			drawMap();
		} else {
			this.setTitle("Koniec działania algorytmu" + "; popełniony błąd: "
					+ error + " / dopuszczalny błąd: "
					+ worldMap.getIndifferenceThreshold());
			removeMouseListener(this);
		}
	}

	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public void componentResized(ComponentEvent e) {
		try {
			redraw();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}

	public void componentMoved(ComponentEvent e) {
	}

	public void componentShown(ComponentEvent e) {
	}

	public void componentHidden(ComponentEvent e) {
	}

	/**
	 * @throws InterruptedException
	 * 
	 */
	private void redraw() throws InterruptedException {
		width = this.getWidth();
		height = this.getHeight();
	}

	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	public void keyReleased(KeyEvent e) {
		if(e.getKeyChar() == ' ') {
			nextStepAction();
		}
	}

}
